package io.lvdaxian.mybatis18.executor;

import com.alibaba.fastjson2.JSON;
import io.lvdaxian.mybatis18.cache.Cache;
import io.lvdaxian.mybatis18.cache.CacheKey;
import io.lvdaxian.mybatis18.cache.TransactionalCacheManager;
import io.lvdaxian.mybatis18.mapping.BoundSql;
import io.lvdaxian.mybatis18.mapping.MappedStatement;
import io.lvdaxian.mybatis18.session.ResultHandler;
import io.lvdaxian.mybatis18.session.RowBounds;
import io.lvdaxian.mybatis18.transaction.Transaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.SQLException;
import java.util.List;

/**
 * 执行器 缓存
 *
 * @author lihh
 */
public class CachingExecutor implements Executor {
  
  // 日志
  private Logger logger = LoggerFactory.getLogger(CachingExecutor.class);
  // 执行器
  private Executor delegate;
  // 这里是 事务缓存管理
  private TransactionalCacheManager tcm = new TransactionalCacheManager();
  
  // delegate 这是执行器
  public CachingExecutor(Executor delegate) {
    this.delegate = delegate;
    delegate.setExecutorWrapper(this);
  }
  
  // 这是执行器 更新处理
  @Override
  public int update(MappedStatement ms, Object parameter) throws SQLException {
    return delegate.update(ms, parameter);
  }
  
  // 执行器的 查询数据
  @Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    // 拿到 MappedStatement 的 cache
    Cache cache = ms.getCache();
    if (cache != null) {
      // 这里是 刷新缓存
      flushCacheIfRequired(ms);
      // isUseCache 这里 设置 是否使用缓存
      // resultHandler 结果处理事件
      if (ms.isUseCache() && resultHandler == null) {
        // 这里 拿到缓存数据
        List<E> list = (List<E>) tcm.getObject(cache, key);
        // 如果为 null 的话，需要重新查询
        if (list == null) {
          list = delegate.<E>query(ms, parameter, rowBounds, null, key, boundSql);
          // cache：缓存队列实现类，FIFO
          // key：哈希值 [mappedStatementId + offset + limit + SQL + queryParams + environment]
          // list：查询的数据
          // 从这里 更新缓存
          tcm.putObject(cache, key, list);
        }
        // 打印调试日志，记录二级缓存获取数据
        if (logger.isDebugEnabled() && cache.getSize() > 0) {
          logger.debug("二级缓存：{}", JSON.toJSONString(list));
        }
        return list;
      }
    }
    // 这里是 查询处理
    return delegate.<E>query(ms, parameter, rowBounds, resultHandler, key, boundSql);
  }
  
  @Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    // 1. 获取绑定SQL
    BoundSql boundSql = ms.getBoundSql(parameter);
    // 2. 创建缓存Key
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
  }
  
  @Override
  public Transaction getTransaction() {
    return delegate.getTransaction();
  }
  
  @Override
  public void commit(boolean required) throws SQLException {
    delegate.commit(required);
    tcm.commit();
  }
  
  @Override
  public void rollback(boolean required) throws SQLException {
    try {
      delegate.rollback(required);
    } finally {
      if (required) {
        tcm.rollback();
      }
    }
  }
  
  @Override
  public void close(boolean forceRollback) {
    try {
      if (forceRollback) {
        tcm.rollback();
      } else {
        tcm.commit();
      }
    } finally {
      delegate.close(forceRollback);
    }
  }
  
  @Override
  public void clearLocalCache() {
    delegate.clearLocalCache();
  }
  
  @Override
  public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
    return delegate.createCacheKey(ms, parameterObject, rowBounds, boundSql);
  }
  
  @Override
  public void setExecutorWrapper(Executor executor) {
    throw new UnsupportedOperationException("This method should not be called");
  }
  
  /**
   * 刷新 缓存的处理
   *
   * @param ms mapped statement 状态
   * @author lihh
   */
  private void flushCacheIfRequired(MappedStatement ms) {
    // 从这里 拿到缓存
    Cache cache = ms.getCache();
    // 如果缓存不为 null && 是刷新缓存
    if (cache != null && ms.isFlushCacheRequired()) {
      // 这里是 刷新缓存
      tcm.clear(cache);
    }
  }
  
}
